iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0
Software Development

C# ASP.NET MVC實作: 30天打造屬於你的網站應用程式系列 第 28

(DAY 28)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-JSON資料格式

  • 分享至 

  • xImage
  •  

30天鐵人賽即將進入尾聲啦~今天來提一個很常用的資料儲存格式:JSON。

● JSON是什麼?

JSON(JavaScript Object Notation)是一種由Key(屬性)和Value(屬性值)所組成的資料格式,格式與使用哪種程式語言無關,許多程式語言都能夠將其解析和字串化,其廣泛使用的程度也使其成為通用的資料格式。

每筆資料的呈現方式說明如下:

  1. 每筆資料外面用{}包住,資料屬性與名稱(字串)用雙引號""括住,中間用冒號:隔開。
  2. 如果為bool或數字型別,則不用雙引號。ex:{"Name":"王大華","Age":18,"IsMarried":false},如果有經過排版通常會將每組Key/Value分行顯示,例如下面這樣:
{
  "Name":"王大華",
  "Age":18,
  "IsMarried":false
}
  1. 當有多筆資料時通常會以陣列格式儲存,全部資料外面用[]包住,每筆資料間用逗點隔開,例如:
[
 {
  "Name":"王大華",
  "Age":18,
  "IsMarried":false
 },

 {
  "Name":"陳小原",
  "Age":43,
  "IsMarried":true
 }
]

了解JSON資料格式之後,接著我們就可以來對資料做一些處理。

● 將JSON資料轉成 C#的類別

從上面JSON陣列格式範例來看,每筆資料有相同的Key(屬性名稱)與對應的Value(值),用C#語言的角度來看就是物件的形式。所以我們假如想要將JSON資料轉成C#程式碼來使用,第一件事就是要依照JSON屬性來建立C#的類別。例如上面的格式我們可以建立一個Member類別如下:

    public class Member
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public bool IsMarried { get; set; }
    }

但如果每筆資料的屬性非常多的話,要一個一個KEY似乎不是有效率的方式,比如點入下方JSON連結來看,這是一個台北市Youbike站點相關的資料,沒有排版的情況下相信光要找有哪些屬性眼睛就脫窗了= =
https://tcgbusfs.blob.core.windows.net/dotapp/youbike/v2/youbike_immediate.json

因此我們可以透過Visual Studio內建的功能,使用更有效率的方式來轉換,前置作業先新增一個ASP.NET Web應用程式(.NET Framework)專案,並選擇MVC範本建立,這邊應該前面做到快爛掉了吧~還不會就趕緊翻前面文章看看啦!

建立完成後操作步驟如下:

  1. Model資料夾新增一個類別(ex:Youbike),並將class內容清空

  2. 將連結的JSON內容全選複製,在類別內選擇性貼上JSON來源

  3. 貼上後Code應該會如下面:

    public class Rootobject
    {
        public Class1[] Property1 { get; set; }
    }

    public class Class1
    {
        public string sno { get; set; }
        public string sna { get; set; }
        public int tot { get; set; }
        public int sbi { get; set; }
        public string sarea { get; set; }
        public string mday { get; set; }
        public float lat { get; set; }
        public float lng { get; set; }
        public string ar { get; set; }
        public string sareaen { get; set; }
        public string snaen { get; set; }
        public string aren { get; set; }
        public int bemp { get; set; }
        public string act { get; set; }
        public string srcUpdateTime { get; set; }
        public string updateTime { get; set; }
        public string infoTime { get; set; }
        public string infoDate { get; set; }
    }
  1. Class1名稱修改成自訂名稱,例如Youbike,上面Rootobject類別表示資料最頂層由一個陣列[]包住底下每筆資料,後續不會用到所以是可以刪除的,修改後Code如下:
namespace JsonDemoWebsite.Models
{
    public class Youbike
    {
        public string sno { get; set; }
        public string sna { get; set; }
        public int tot { get; set; }
        public int sbi { get; set; }
        public string sarea { get; set; }
        public string mday { get; set; }
        public float lat { get; set; }
        public float lng { get; set; }
        public string ar { get; set; }
        public string sareaen { get; set; }
        public string snaen { get; set; }
        public string aren { get; set; }
        public int bemp { get; set; }
        public string act { get; set; }
        public string srcUpdateTime { get; set; }
        public string updateTime { get; set; }
        public string infoTime { get; set; }
        public string infoDate { get; set; }
    }

}

● 範例-Youbike站點清單列表

以下的範例會使用Newtonsoft.Json這個套件來轉換JSON資料,首先打開NuGet套件管理員確認是否已經有安裝該套件,沒有的話就先安裝。

接著說明如何獲取網址連結的JSON資料,首先Client端要發出HTTP請求到Server端,Server端收到請求後會回應JSON資料內容,再由Client端接收。

接收到資料後,透過套件幫助將JSON格式轉換成我們要的C#語言內容,這段稱為「反序列化(Deserialize)」。此範例我們會將資料轉換成List<Youbike>型別,拿到List物件後,後續就和之前講過的方式一樣,可以作為model傳入View顯示等等。

了解後我們就來實作Code吧!步驟參考如下:

  1. Model資料夾新增一個類別名為JsonService,建立後將class宣告為static,方便後續不用再new物件出來。

  2. JsonService內加入以下Code,這裡建立一個非同步方法來獲取JSON資料,並反序列化成List<Youbike>物件回傳。

        public static async Task<List<Youbike>> GetDataAsync_Youbike()
        {
            List<Youbike> result = new List<Youbike>();
            using (var client = new HttpClient())
            using (var request = new HttpRequestMessage())
            {
                request.Method = HttpMethod.Get;
                request.RequestUri = new Uri("https://tcgbusfs.blob.core.windows.net/dotapp/youbike/v2/youbike_immediate.json");
                var response = await client.SendAsync(request);
                var responseBody = await response.Content.ReadAsStringAsync();
                result = JsonConvert.DeserializeObject<List<Youbike>>(responseBody);
            }
            return result;
        }
  1. Controller資料夾內新建控制器,名為JsonController,將內建Index()方法修改為下列Code:
    public class JsonController : Controller
    {
        // GET: Json
        public async Task<ActionResult> Index() //using System.Threading.Tasks;
        {
            var model = await JsonService.GetDataAsync_Youbike();
            return View(model);
        }
    }
  1. 新增對應的View內容,這邊使用List範本與Youbike模型快速建立。

  2. 建立後Code與顯示頁面如下,可以比照前面幾天的範例去美化或調整顯示內容,這邊就不再贅述了。

@model IEnumerable<JsonDemoWebsite.Models.Youbike>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.sno)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.sna)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.tot)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.sbi)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.sarea)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.mday)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.lat)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.lng)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ar)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.sareaen)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.snaen)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.aren)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.bemp)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.act)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.srcUpdateTime)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.updateTime)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.infoTime)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.infoDate)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.sno)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.sna)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.tot)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.sbi)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.sarea)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.mday)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.lat)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.lng)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.ar)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.sareaen)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.snaen)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.aren)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.bemp)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.act)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.srcUpdateTime)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.updateTime)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.infoTime)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.infoDate)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
            @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
            @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
        </td>
    </tr>
}

</table>

● 範例-多層巢狀結構資料轉換

有的時候網路上的JSON資料不見得都是只有一層結構,例如下方連結是交通部觀光局提供的觀光景點JSON檔案。
https://gis.taiwan.net.tw/XMLReleaseALL_public/scenic_spot_C_f.json

從內容可以看到最外層是"XML_Head"屬性,在"XML_Head"裡面有"Listname""Language""Orgname""Updatetime""Infos",而實際上每筆景點的資料是在"Infos"裡面的"Info"當中。當我們將資料全選複製並選擇性貼上類別時,會產生如下面Code:

    public class Rootobject
    {
        public XML_Head XML_Head { get; set; }
    }

    public class XML_Head
    {
        public string Listname { get; set; }
        public string Language { get; set; }
        public string Orgname { get; set; }
        public DateTime Updatetime { get; set; }
        public Infos Infos { get; set; }
    }

    public class Infos
    {
        public Info[] Info { get; set; }
    }

    public class Info
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Zone { get; set; }
        public string Toldescribe { get; set; }
        public string Description { get; set; }
        public string Tel { get; set; }
        public string Add { get; set; }
        public string Zipcode { get; set; }
        public string Region { get; set; }
        public string Town { get; set; }
        public string Travellinginfo { get; set; }
        public string Opentime { get; set; }
        public string Picture1 { get; set; }
        public string Picdescribe1 { get; set; }
        public string Picture2 { get; set; }
        public string Picdescribe2 { get; set; }
        public string Picture3 { get; set; }
        public string Picdescribe3 { get; set; }
        public string Map { get; set; }
        public string Gov { get; set; }
        public float Px { get; set; }
        public float Py { get; set; }
        public string Orgclass { get; set; }
        public string Class1 { get; set; }
        public string Class2 { get; set; }
        public string Class3 { get; set; }
        public string Level { get; set; }
        public string Website { get; set; }
        public string Parkinginfo { get; set; }
        public float? Parkinginfo_Px { get; set; }
        public float? Parkinginfo_Py { get; set; }
        public string Ticketinfo { get; set; }
        public string Remarks { get; set; }
        public string Keyword { get; set; }
        public DateTime? Changetime { get; set; }
    }

可以看到開發工具會逐層產生對應的類別與屬性,我們比照上面範例新建一個方法來請求資料,這時候回傳的內容就不會像Youbike範例那麼單純了,需要抽絲剝繭般找到下層真正要的資料,作法參考下面Code,可以與Youbike範例比較在取得responseBody後將資料反序列化的差異。Controller和View的呈現就不多提,重點在於如何取得正確資料。

        public static async Task<List<TravelPlace>> GetDataAsync_TravelPlace()
        {
            List<TravelPlace> result = new List<TravelPlace>();
            using (var client = new HttpClient())
            using (var request = new HttpRequestMessage())
            {
                request.Method = HttpMethod.Get;
                request.RequestUri = new Uri("https://gis.taiwan.net.tw/XMLReleaseALL_public/scenic_spot_C_f.json");
                var response = await client.SendAsync(request);
                var responseBody = await response.Content.ReadAsStringAsync();
                
                //先反序列化成JSON物件
                var JsonObject = JsonConvert.DeserializeObject<JObject>(responseBody);
                
                //抓取Info裡面的資料並轉換成<List<TravelPlace>>物件
                var token = JsonObject["XML_Head"]["Infos"]["Info"];
                result = token.ToObject<List<TravelPlace>>();
            }
            return result;
        }

● 範例-高雄市公車式小黃列表

最後再提供一個範例參考,這個範例會稍微將View頁面美化過,另外因為資料屬性有string 也有string[],在處理View的時候也要特別注意,當套用預設範本時是不會自動生成string[]相關HTML標籤的,得自行處理加入內容。
完整做法請參考下方Code,也可以先自行練習看看如何呈現,與上述兩個範例差異不大,所以這邊就不再贅述。

公車式小黃
高雄市公車式小黃JSON資料

    public class Rootobject
    {
        public BusTaxi[] Property1 { get; set; }
    }

    public class BusTaxi
    {
        public string name { get; set; }
        public string company { get; set; }
        public string[] tel { get; set; }
        public string imgSrc { get; set; }
        public string[] zone { get; set; }
        public string[] route { get; set; }
        public string[] length { get; set; }
        public string[] shift { get; set; }
    }
    public class BusTaxi
    {
        [DisplayName("路線")]
        public string name { get; set; }

        [DisplayName("車隊")]
        public string company { get; set; }

        [DisplayName("連絡電話")]
        public string[] tel { get; set; }

        [DisplayName("圖片")]
        public string imgSrc { get; set; }

        [DisplayName("地區")]
        public string[] zone { get; set; }

        [DisplayName("起訖站")]
        public string[] route { get; set; }

        [DisplayName("路線長度")]
        public string[] length { get; set; }

        [DisplayName("班次")]
        public string[] shift { get; set; }
    }
        public static async Task<List<BusTaxi>> GetDataAsync_BusTaxi()
        {
            List<BusTaxi> result = new List<BusTaxi>();
            using (var client = new HttpClient())
            using (var request = new HttpRequestMessage())
            {
                var url = "https://data.kcg.gov.tw/dataset/82ef292a-5e96-4c99-a1ca-acae712e3805/resource/482fbc04-8666-4031-8dcf-13b5b4d35860/download/bustaxi.json";
                request.Method = HttpMethod.Get;
                request.RequestUri = new Uri(url);
                var response = await client.SendAsync(request);
                var responseBody = await response.Content.ReadAsStringAsync();
                result = JsonConvert.DeserializeObject<List<BusTaxi>>(responseBody);
            }
            return result;
        }
        public async Task<ActionResult> BusTaxi()
        {
            var model = await JSONService.GetDataAsync_BusTaxi();
            return View(model);
        }
@model IEnumerable<JsonDemoWebsite.Models.BusTaxi>

@{
    ViewBag.Title = "BusTaxi";
}

<h2>BusTaxi</h2>

<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.company)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.imgSrc)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.zone)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.route)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.length)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.shift)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.tel)
        </th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.company)
        </td>
        <td>
            <a href="@item.imgSrc" target="_blank"><img src="@item.imgSrc" style="width:200px;height:150px;" /></a>
        </td>
        <td>
            @foreach (var arrayItem in item.zone)
            {
                <div>
                    @arrayItem <br />
                </div>
            }
        </td>
        <td>
            @foreach (var arrayItem in item.route)
            {
                <div>
                    @arrayItem <br />
                </div>
            }
        </td>
        <td>
            @foreach (var arrayItem in item.length)
            {
                <div>
                    @arrayItem <br />
                </div>
            }
        </td>
        <td>
            @foreach (var arrayItem in item.shift)
            {
                <div>
                    @arrayItem <br />
                </div>
            }
        </td>
        <td>
            @foreach (var arrayItem in item.tel)
            {
                <div>
                    @arrayItem <br />
                </div>
            }
        </td>
    </tr>
}

</table>

● 小結

那麼今天就介紹到這邊啦~明天會接續今天內容來介紹好用的套件:PagedList,See you Tomorrow~


上一篇
(DAY 27)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-自訂Helper
下一篇
(DAY 29)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-PagedList套件使用
系列文
C# ASP.NET MVC實作: 30天打造屬於你的網站應用程式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言